home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir41 / tsrsrc35.zip / DEVICE.PAS next >
Pascal/Delphi Source File  |  1993-10-19  |  11KB  |  409 lines

  1. {
  2.  Display the DOS device driver chain.
  3.  Adapted from an assembly language program by Ray Duncan and modified by
  4.  several others.
  5.  
  6.  version 3.0 9/2/91
  7.    reorganize source code for consistency with other utilities
  8.  version 3.1 11/4/91
  9.    no change
  10.  version 3.2 11/22/91
  11.    no change
  12.  version 3.3 1/8/92
  13.    increase stack space
  14.    new features for parsing and getting command line options
  15.  version 3.4 2/14/92
  16.    no change
  17.  version 3.5 10/18/93
  18.    display MSCDEX CD-ROM drive letters
  19. }
  20.  
  21. {$R-,S-,I-,V-,B-,F-,A-,E-,N-,G-,X-}
  22. {$M 4096,0,655360}
  23.  
  24. program Device_Chain;
  25.  
  26. uses
  27.   Dos,
  28.   MemU;
  29.  
  30. const
  31.   MaxDevices = 100;               {Maximum number of devices to report}
  32.  
  33. type
  34.   {FCB used to find start of device driver chain}
  35.   FileControlBlock =
  36.     record
  37.       Drive : Byte;
  38.       Filename : array[1..8] of Char;
  39.       Extension : array[1..3] of Char;
  40.       CurrentBl : Word;
  41.       LRL : Word;
  42.       FilSizeLo : Word;
  43.       FilSizeHi : Word;
  44.       FileDate : Word;
  45.       FileTime : Word;
  46.       Other : array[0..7] of Byte;
  47.       CurRecord : Byte;
  48.       RelRecLo : Word;
  49.       RelRecHi : Word;
  50.     end;
  51.  
  52.   DisplayRec =
  53.     record
  54.       StartAddr : Pointer;
  55.       Header : CDROMDeviceHeader;
  56.     end;
  57.   DisplayArray = array[1..MaxDevices] of DisplayRec;
  58.  
  59. var
  60.   DeviceControlBlock : FileControlBlock; {File Control Block for NUL Device}
  61.   DevicePtr : ^CDROMDeviceHeader; {Pointer to the next device header}
  62.   DeviceSegment : Word;           {Current device segment}
  63.   DeviceOffset : Word;            {Current device offset}
  64.   DeviceCount : Word;             {Number of devices}
  65.   Devices : DisplayArray;         {Sortable list of devices}
  66.   RawMode : Boolean;
  67.   NulStatus : Byte;
  68.  
  69.   procedure Abort(Msg : String);
  70.   begin
  71.     WriteLn(Msg);
  72.     Halt(1);
  73.   end;
  74.  
  75.   function FindNulDevice(Segm : Word) : Word;
  76.     {-Return the offset of the null device in the specified segment}
  77.   var
  78.     Ofst : Word;
  79.   begin
  80.     for Ofst := 0 to 65534 do
  81.       if MemW[Segm:Ofst] = $554E then
  82.         {Starts with 'NU'}
  83.         if Mem[Segm:Ofst+2] = Byte('L') then
  84.           {Continues with 'L'}
  85.           if (MemW[Segm:Ofst-6] and $801F) = $8004 then begin
  86.             {Has correct driver attribute}
  87.             FindNulDevice := Ofst-10;
  88.             Exit;
  89.           end;
  90.     Abort('Cannot find NUL device driver');
  91.   end;
  92.  
  93. var
  94.   Pivot : DisplayRec;
  95.   Swap : DisplayRec;
  96.  
  97.   function PhysAddr(X : Pointer) : LongInt;
  98.     {-Return the physical address given by pointer X}
  99.   begin
  100.     PhysAddr := (LongInt(OS(X).S) shl 4)+OS(X).O;
  101.   end;
  102.  
  103.   function Less(X, Y : DisplayRec) : Boolean;
  104.     {-Return True if address of X is less than address of Y}
  105.   begin
  106.     Less := (PhysAddr(X.StartAddr) < PhysAddr(Y.StartAddr));
  107.   end;
  108.  
  109.   procedure Sort(L, R : Word);
  110.     {-Sort device headers}
  111.   var
  112.     I : Word;
  113.     J : Word;
  114.   begin
  115.     I := L;
  116.     J := R;
  117.     Pivot := Devices[(L+R) shr 1];
  118.     repeat
  119.       {Sort by address}
  120.       while Less(Devices[I], Pivot) do
  121.         Inc(I);
  122.       while Less(Pivot, Devices[J]) do
  123.         Dec(J);
  124.       if I <= J then begin
  125.         Swap := Devices[J];
  126.         Devices[J] := Devices[I];
  127.         Devices[I] := Swap;
  128.         Inc(I);
  129.         Dec(J);
  130.       end;
  131.     until I > J;
  132.     if L < J then
  133.       Sort(L, J);
  134.     if I < R then
  135.       Sort(I, R);
  136.   end;
  137.  
  138.   procedure WriteHelp;
  139.     {-Write a simple help screen}
  140.   begin
  141.     WriteLn;
  142.     WriteLn('DEVICE produces a report showing the device drivers loaded into the system as');
  143.     WriteLn('well as how much memory each uses, and what interrupt vectors are taken over.');
  144.     WriteLn;
  145.     WriteLn('DEVICE accepts the following command line syntax:');
  146.     WriteLn;
  147.     WriteLn('  DEVICE [Options]');
  148.     WriteLn;
  149.     WriteLn('Options may be preceded by either / or -. Valid options are as follows:');
  150.     WriteLn('     /R     raw, unsorted report.');
  151.     WriteLn('     /?     write help screen.');
  152.     Halt(1);
  153.   end;
  154.  
  155.   procedure GetOptions;
  156.     {-Check for command line options}
  157.   var
  158.     Arg : String[127];
  159.  
  160.     procedure GetArgs(S : String);
  161.     var
  162.       SPos : Word;
  163.     begin
  164.       SPos := 1;
  165.       repeat
  166.         Arg := NextArg(S, SPos);
  167.         if Arg = '' then
  168.           Exit;
  169.         if Length(Arg) = 2 then
  170.           if (Arg[1] = '/') or (Arg[1] = '-') then
  171.             case Upcase(Arg[2]) of
  172.               'R' : RawMode := True;
  173.               '?' : WriteHelp;
  174.             end;
  175.       until False;
  176.     end;
  177.  
  178.   begin
  179.     RawMode := False;
  180.  
  181.     {Get arguments from the command line and the environment}
  182.     GetArgs(StringPtr(Ptr(PrefixSeg, $80))^);
  183.     GetArgs(GetEnv('DEVICE'));
  184.   end;
  185.  
  186.   function GetName(Header : CDROMDeviceHeader) : String;
  187.     {-Get a device name}
  188.   const
  189.     Plural : array[Boolean] of String[1] = ('', 's');
  190.   var
  191.     Num : String[3];
  192.   begin
  193.     with Header do
  194.       if (Attributes and $8000) <> 0 then begin
  195.         if (Attributes = $C800) and (Header.DriveLet <> 0) then
  196.           {An MSCDEX CD-ROM}
  197.           GetName := DeviceName+'('+
  198.                      Char(Byte('A')+Header.DriveLet-1)+':)'
  199.         else
  200.           GetName := DeviceName;
  201.       end else begin
  202.         Str(Ord(DeviceName[1]), Num);
  203.         GetName := Num+' Block Unit'+Plural[Ord(DeviceName[1]) <> 1];
  204.       end;
  205.   end;
  206.  
  207.   procedure RawReport;
  208.     {-Raw, unsorted device report}
  209.   var
  210.     D : Word;
  211.   begin
  212.     WriteLn;
  213.     WriteLn(' Starting      Next             Strategy   Interrupt   Device');
  214.     WriteLn(' Address     Hdr Addr   Attr   Entry Pnt   Entry Pnt   Name');
  215.     WriteLn('---------   ---------   ----   ---------   ---------   --------');
  216.  
  217.     for D := 1 to DeviceCount do
  218.       with Devices[D], Header do
  219.         WriteLn(HexPtr(StartAddr), '   ',
  220.                 HexW(NextHeaderSegment), ':', HexW(NextHeaderOffset), '   ',
  221.                 HexW(Attributes), '   ',
  222.                 HexW(DeviceSegment), ':', HexW(StrategyEntPt), '   ',
  223.                 HexW(DeviceSegment), ':', HexW(InterruptEntPt), '   ',
  224.                 GetName(Header));
  225.   end;
  226.  
  227.   function GetCommandPtr(DosPtr : DosRecPtr) : Pointer;
  228.     {-Get the address of COMMAND.COM}
  229.   type
  230.     McbRec =
  231.       record
  232.         ID : Char;
  233.         PSPSeg : Word;
  234.         Len : Word;
  235.       end;
  236.   var
  237.     McbPtr : ^McbRec;
  238.   begin
  239.     McbPtr := Ptr(DosPtr^.McbSeg, 0);
  240.     McbPtr := Ptr(OS(McbPtr).S+McbPtr^.Len+1, 0);
  241.     GetCommandPtr := Ptr(McbPtr^.PSPSeg, 0);
  242.   end;
  243.  
  244.   procedure WriteDevice(StartAddr : Pointer;
  245.                         Name : String;
  246.                         Start, Stop : LongInt;
  247.                         ShowVecs : Boolean);
  248.     {-Write data for one device}
  249.   var
  250.     Size : LongInt;
  251.     VecAddr : LongInt;
  252.     Vec : Byte;
  253.     Cnt : Byte;
  254.     BPtr : ^Byte;
  255.   begin
  256.     Size := Stop-Start;
  257.     ShowVecs := ShowVecs and (Size <> 0);
  258.  
  259.     Write(HexPtr(StartAddr), '   ');
  260.     if Size <> 0 then
  261.       Write(Size:6)
  262.     else
  263.       Write('     -');
  264.     if ShowVecs then
  265.       while Length(Name) < 14 do
  266.         Name := Name+' ';
  267.     Write('   ', Name);
  268.  
  269.     if ShowVecs then begin
  270.       Cnt := 0;
  271.       for Vec := 0 to $80 {!!} do begin
  272.         VecAddr := PhysAddr(Pointer(MemL[0:4*Vec]));
  273.         if (VecAddr >= Start) and (VecAddr < Stop) then
  274.           {Points to this memory block}
  275.           if Byte(Pointer(VecAddr)^) <> $CF then begin
  276.             {Doesn't point to IRET}
  277.             if Cnt >= 12 then begin
  278.               WriteLn;
  279.               Write('                                   ');
  280.               Cnt := 0;
  281.             end;
  282.             inc(Cnt);
  283.             Write(' ', HexB(Vec));
  284.           end;
  285.       end;
  286.     end;
  287.     WriteLn;
  288.   end;
  289.  
  290.   procedure SortedReport;
  291.     {-Sorted report better for user consumption}
  292.   const
  293.     NulDevice : array[1..8] of Char = 'NUL     ';
  294.   var
  295.     D : Word;
  296.     DosCode : Pointer;
  297.     CommandPtr : Pointer;
  298.     DosPtr : DosRecPtr;
  299.     DosBuffers : SftRecPtr;
  300.     Start : LongInt;
  301.     Stop : LongInt;
  302.     FoundNul : Boolean;
  303.   begin
  304.     {Pointer to DOS variables}
  305.     DosPtr := Ptr(OS(DosList).S, OS(DosList).O-2);
  306.  
  307.     {Get the address of the lowest DOS code}
  308.     DosCode := Ptr(OS(Devices[1].StartAddr).S, 0);
  309.  
  310.     {Get the address of the start of DOS's file tables}
  311.     DosBuffers := DosPtr^.FirstSFT^.Next;
  312.  
  313.     {Get pointer to command.com}
  314.     CommandPtr := GetCommandPtr(DosPtr);
  315.  
  316.     WriteLn;
  317.     WriteLn(' Address     Bytes   Name           Hooked vectors');
  318.     WriteLn('---------   ------   -------------- --------------');
  319.     {        ssss:oooo   ssssss   nnnnnnnn       xx xx xx xx xx}
  320.  
  321.     {Display the devices}
  322.     FoundNul := False;
  323.     for D := 1 to DeviceCount-1 do begin
  324.       if FoundNul then begin
  325.         Start := PhysAddr(Devices[D].StartAddr);
  326.         Stop := PhysAddr(Devices[D+1].StartAddr);
  327.       end else if GetName(Devices[D].Header) = NulDevice then begin
  328.         FoundNul := True;
  329.         Start := PhysAddr(DosCode);
  330.         Stop := PhysAddr(Devices[D+1].StartAddr);
  331.       end else begin
  332.         Start := 0;
  333.         Stop := 0;
  334.       end;
  335.       {Protect against devices patched in after DOS}
  336.       if Stop > PhysAddr(DosBuffers) then begin
  337.         WriteLn('Detected device drivers patched in after CONFIG.SYS');
  338.         Exit;
  339.       end;
  340.       with Devices[D] do
  341.         WriteDevice(StartAddr, GetName(Header), Start, Stop, True);
  342.     end;
  343.  
  344.     {Last device}
  345.     with Devices[DeviceCount] do begin
  346.       Start := PhysAddr(StartAddr);
  347.       Stop := PhysAddr(DosBuffers);
  348.       WriteDevice(StartAddr, GetName(Header), Start, Stop, True);
  349.     end;
  350.  
  351.     {DOS buffers}
  352.     Start := PhysAddr(DosBuffers);
  353.     Stop := PhysAddr(CommandPtr);
  354.     WriteDevice(DosBuffers, 'DOS buffers', Start, Stop, False);
  355.   end;
  356.  
  357. begin
  358.   WriteLn('DEVICE ', Version, ', Copyright 1993 TurboPower Software');
  359.  
  360.   GetOptions;
  361.  
  362.   {Find the start of the device driver chain via the NUL device}
  363.   FillChar(DeviceControlBlock, SizeOf(DeviceControlBlock), 0);
  364.   with DeviceControlBlock do begin
  365.     Filename := 'NUL     ';
  366.     Extension := '   ';
  367.     asm
  368.       mov ax,$0F00
  369.       mov dx,offset devicecontrolblock
  370.       int $21
  371.       mov NulStatus,al
  372.     end;
  373.     if NulStatus <> 0 then
  374.       Abort('Error opening the NUL device');
  375.     if Hi(DosVersion) > 2 then begin
  376.       {DOS 3.0 or later}
  377.       DeviceSegment := 0;
  378.       DeviceOffset := FindNulDevice(DeviceSegment);
  379.     end else begin
  380.       {DOS 2.x}
  381.       DeviceOffset := Word(Pointer(@Other[1])^);
  382.       DeviceSegment := Word(Pointer(@Other[3])^);
  383.     end;
  384.     DevicePtr := Ptr(DeviceSegment, DeviceOffset);
  385.   end;
  386.  
  387.   {Scan the chain, building an array}
  388.   DeviceCount := 0;
  389.   while OS(DevicePtr).O <> $FFFF do begin
  390.     if DeviceCount < MaxDevices then begin
  391.       Inc(DeviceCount);
  392.       with Devices[DeviceCount] do begin
  393.         StartAddr := Pointer(DevicePtr);
  394.         Header := DevicePtr^;
  395.       end;
  396.     end;
  397.     with DevicePtr^ do
  398.       DevicePtr := Ptr(NextHeaderSegment, NextHeaderOffset);
  399.   end;
  400.  
  401.   if RawMode then
  402.     RawReport
  403.   else begin
  404.     {Sort the array in order of starting address}
  405.     Sort(1, DeviceCount);
  406.     SortedReport;
  407.   end;
  408. end.
  409.